Skip to main content

05 - 评测与复盘

训练跑完了,你手上有一个 LoRA adapter。这一篇把它变成一个数字,再把数字变成下一轮该做什么。

一、合权重并复测

llamafactory-cli export --model_name_or_path Qwen/Qwen3-4B-Instruct-2507 \
--adapter_name_or_path saves/qwen3-4b-agent/lora/sft \
--template qwen3_nothink --export_dir merged/qwen3-4b-agent

用哪个 checkpoint:不是最后一个,是验证 loss 最低的那个(04 篇记录表里那一行)。最后一个通常已经过拟合了。

重起 vLLM 指向合并后的目录,采样参数和量基线时一字不差

vllm serve merged/qwen3-4b-agent --served-model-name qwen3-4b-agent \
--max-model-len 8192 --gpu-memory-utilization 0.85 \
--enable-auto-tool-choice --tool-call-parser hermes

python eval_agent.py --model qwen3-4b-agent --tasks eval_tasks.jsonl --out after.json
python -c "
import json
b, a = json.load(open('baseline.json')), json.load(open('after.json'))
for k in b['summary']:
print(f\"{k:<12} {b['summary'][k]:>6.1%} -> {a['summary'][k]:>6.1%} {a['summary'][k]-b['summary'][k]:+.1%}\")"

二、怎么读这四个差值

四层是逐级依赖的,所以从左往右看,第一个明显偏低的地方就是当前瓶颈

一种要特别警惕的结果:L1 从 40% 涨到 99%,L4 一动没动。

这不是训练失败,是 01 篇 3.1 讲的「右边那条路」—— 格式确实修好了,但判断层没学到东西。它看起来像成功(有个指标大涨),实际上模型该调工具的时候还是不调。

三、失败样本必须逐条读

eval_agent.py 把每条的完整 trace 存进了 after.json。别只看汇总数字。

python -c "
import json
d = json.load(open('after.json'))
for r in d['rows']:
if not r['L4_task']:
print(r['id'], r['cat'], {k: r[k] for k in r if k.startswith('L')})
print(' 最后一句:', (r['trace'][-1].get('content') or '')[:80], '\n')"

cat 归类,看失败集中在哪一类:

失败集中在说明下一轮怎么补
「不该调工具」模型学成了见问题就调工具训练集里这类样本太少,加到 10% 左右
「工具会报错」报错后卡住不动报错纠正类占比不够,回 02 篇把 KEEP_PER_TASK 调大
「连调两次」只调一次就编答案多步任务样本不足
「多轮带指代」第二轮丢了上下文检查 cutoff_len 是不是把长轨迹截了

这张表是这个项目真正的产出。 分数只告诉你好没好,它告诉你下一步做什么。

四、补数据还是调超参

绝大多数情况是补数据。判据很简单:

现象结论
某一类任务系统性失败补数据。这类样本在训练集里不够
各类都涨了一点但都不多补数据。总量不够,从 3000 加到 6000
train loss 降得很好,val loss 早早抬头调超参。epoch 减到抬头之前
train loss 一直下不去调超参。lora_rank 提到 32,或 lr 提到 2e-4

「各类都涨一点」和「某一类完全没涨」是两种病,别用同一种药。前者是量的问题,后者是覆盖面的问题 —— 后者哪怕把数据翻十倍也不会好,因为缺的那类样本翻十倍还是没有。

五、一轮下来的账

用的 checkpoint(第几步)待填
L1 基线 → 训后待填
L2 基线 → 训后待填
L3 基线 → 训后待填
L4 基线 → 训后待填
失败最集中的类别待填
本轮结论:补数据 / 调超参待填
累计机时与花费待填
蒸馏 API 花费待填

填完这张表,第二轮就是回到 02 篇按倒数第二行的结论补数据,其余步骤原样再走一遍。

第二轮通常比第一轮便宜得多 —— 数据脚本、评测脚本、配置文件都不用再写,只是重跑。